/*
 * TODO language 
 * Copyright (c) 2010 TODO Team
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>
#include "API.h"
#include "types.h"
#include "str.h"

namespace todo { namespace type {
	
String::String() : Type("string", TYPE_P("object"))
{
}

void String::load(void)
{
	// Methods
	register_method(new todo::Method(TODO_PUBLIC, "string", "charAt",    1, TypeList(1, "int"), (MethodPtr)&String::char_at));
	register_method(new todo::Method(TODO_PUBLIC, "int",    "find",      1, TypeList(1, "string"), (MethodPtr)&String::find));
	register_method(new todo::Method(TODO_PUBLIC, "string", "__slice__", 2, TypeList(2, "int", "int"), (MethodPtr)&String::slice));
	register_method(new todo::Method(TODO_PUBLIC, "string", "__subscript__", 1, TypeList(1, "int"), (MethodPtr)&String::char_at));
	register_method(new todo::Method(TODO_PUBLIC, "string", "toString",  0, NULL, (MethodPtr)&String::toString));
	register_method(new todo::Method(TODO_PUBLIC, "string", "toUpper",   0, NULL, (MethodPtr)&String::toUpper));
	register_method(new todo::Method(TODO_PUBLIC, "string", "toLower",   0, NULL, (MethodPtr)&String::toLower));
	register_method(new todo::Method(TODO_PUBLIC, "string", "trim",      0, NULL, (MethodPtr)&String::trim));
	register_method(new todo::Method(TODO_PUBLIC, "int",    "length",    0, NULL, (MethodPtr)&String::length));
	register_method(new todo::Method(TODO_PUBLIC, "string", "__plus__",  1, TypeList(1, "string"), (MethodPtr)&String::concat));
	register_method(new todo::Method(TODO_PUBLIC, "string", "__equal__",  1, TypeList(1, "string"), (MethodPtr)&String::equal));
	register_method(new todo::Method(TODO_PUBLIC, "string", "__not_equal__",  1, TypeList(1, "string"), (MethodPtr)&String::is_not_equal));

	// Static methods
	register_method(new todo::StaticMethod(TODO_PUBLIC, "string", "format", 2, TypeList(2, "string", "vararg"), (StaticMethodPtr)&String::format));
}

/**
 * bool string::__not_equal__( string str )
 */
TODO_METHOD(String, is_not_equal)
{
	Value* ret = NEW("bool");
	
	BOOL_V(ret) = STR_P(value)->compare(STR_V(ARG(0))) != 0 ? true : false;
	
	return ret;
}

/**
 * bool string::__equal__( string str )
 */
TODO_METHOD(String, equal)
{
	Value* ret = NEW("bool");
	
	BOOL_V(ret) = STR_P(value)->compare(STR_V(ARG(0))) == 0 ? true : false;
	
	return ret;
}

/**
 * string string::charAt( int position )
 * string string::__subscript__( int position )
 */
TODO_METHOD(String, char_at)
{	
	long at = INT_V(ARG(0));
	Value *val = NEW("string");
	
	STR_V(val) = STR_P(value)->at(at);
	
	return val; 
}

/**
 * string string::__plus__( string value )
 */
TODO_METHOD(String, concat)
{
	Value* ret = copy(value);
	
	STR_P(ret)->append(STR_V(ARG(0)));
	
	return ret;
}

/**
 * string string::toString( void )
 */
TODO_METHOD(String, toString)
{
	Value* ret = copy(value);
	
	return ret;
}

/**
 * string string::trim( string str )
 */
TODO_METHOD(String, trim)
{
	std::string* obj = STR_P(value);
	Value *val = alloc();
	std::string* ret = STR_P(val);

	int start, end, size = obj->size();
	
	for (start = 0; start < size && isspace(obj->at(start)); ++start);
	for (end = size - 1; end >= start && isspace(obj->at(end)); --end);
	
	*ret = obj->substr(start, end - start + 1);
	
	return val;
}

/**
 * string string::toUpper( void )
 */
TODO_METHOD(String, toUpper)
{
	Value *val = copy(value);
	std::string* str = STR_P(val);

	*str = STR_V(value);

	for (unsigned int i = 0; i < str->length(); ++i) {
		(*str)[i] = toupper((*str)[i]);
	}
	
	return val;
}

/**
 * string string::toLower( void )
 */
TODO_METHOD(String, toLower)
{
	Value *val = copy(value);
	std::string* str = STR_P(val);

	*str = STR_V(value);

	for (unsigned int i = 0; i < str->length(); ++i) {
		(*str)[i] = tolower((*str)[i]);
	}
	
	return val;
}

/**
 * int string::find( string needed )
 */
TODO_METHOD(String, find)
{
	std::string* needed = STR_P(ARG(0));
	Value *val = NEW("int");
	
	INT_V(val) = STR_P(value)->find(*needed);

	return val;
}

/**
 * int string::length( void )
 */
TODO_METHOD(String, length)
{
	Value *val = NEW("int");
	
	INT_V(val) = STR_P(value)->length();

	return val;
}

/**
 * string string::format( string format, ... )
 */
TODO_STATIC_METHOD(String, format)
{
	char str[30];
	const char* format = STR_P(ARG(0))->c_str();
	long num = INT_V(ARG(1));
	Value* val = NEW("string");
	
	sprintf(str, format, num);
	
	STR_P(val)->assign(str);

	return val;
}

/**
 * string string::__slice__( int initial, int end )
 */
TODO_METHOD(String, slice)
{
	Value* val = copy(value);
	long x = INT_V(ARG(0));
	long y = INT_V(ARG(1));
	
	if (x < 0) {
		x = STR_P(val)->length() - (x < 0 ? -x : x);
	}
	if (y == 0) {
		y = STR_P(val)->length();
	} else if (y < 0) {
		y = STR_P(val)->length() - (y < 0 ? -y : y) - x;
	}
	
	STR_P(val)->assign(STR_P(val)->substr(x, y));
	
	return val;
}

/**
 * Allocs a string value
 */
Value* String::alloc()
{
	Value *value = new Value(this, new std::string());
	
	value->parent = parent->alloc();
	alloc_value_properties(value);
	
	return value;
}

/**
 * Copies a string value
 */
Value* String::copy(const Value* value)
{
	Value* new_value = new Value(this, new std::string(STR_V(value)));
	
	new_value->parent = parent->alloc();	
	alloc_value_properties(new_value);
	
	return new_value;	
}

/**
 * Deallocs a string value
 */
void String::dealloc(Value* value)
{
	parent->dealloc(value->parent);
	delete STR_P(value);
	delete value;
}
	
}} // todo::type
